// SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
/* Copyright (C) 2016-2018, LomboTech Co.Ltd.
 * Author: lomboswer <lomboswer@lombotech.com>
 *
 * Lombo VISS-MIPI-CSI controller register definitions
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 */


/******************************************************************************
 * Controller clock and reset configuration
 *****************************************************************************/
#include <linux/module.h>
#include <linux/delay.h>
#include <mach/csp.h>
#include "../viss-const.h"
#include "mipi_csi_csp.h"

/* #define ENABLE_TRACE */
#include <mach/debug.h>
/**
 * MIPI_CSI enable
 */
u32 csp_mcsi_enable(void *base)

{
	reg_mipi_csi_mcsi_ctrl_t tmpreg;
	unsigned long reg_addr = 0;
	reg_mipi_csi_t *g_mipi_csi = (reg_mipi_csi_t *)base;

	PRT_TRACE_BEGIN("\n");
	reg_addr = (unsigned long)&g_mipi_csi->mcsi_ctrl;
	tmpreg.val = 0;
	tmpreg.bits.en = 1;
	WRITEREG32(reg_addr, tmpreg.val);
	PRT_TRACE_END("\n");

	return 0;
}

/**
 * MIPI_CSI disable
 */
u32 csp_mcsi_disable(void *base)
{
	reg_mipi_csi_mcsi_ctrl_t tmpreg;
	unsigned long reg_addr = 0;
	reg_mipi_csi_t *g_mipi_csi = (reg_mipi_csi_t *)base;

	PRT_TRACE_BEGIN("\n");
	reg_addr = (unsigned long)&g_mipi_csi->mcsi_ctrl;
	tmpreg.val = 0;
	tmpreg.bits.en = 0;
	WRITEREG32(reg_addr, tmpreg.val);
	PRT_TRACE_END("\n");
	return 0;
}
/**
 * MIPI_CSI status
 * @busy:
 *	return 0: free
 *	return 1: busy
 * Before disable DMA or MIPI_CSI, the busy status should be 0
 */
u32 csp_mcsi_status(void *base)
{
	reg_mipi_csi_mcsi_cfg_t tmpreg;
	reg_mipi_csi_t *g_mipi_csi = (reg_mipi_csi_t *)base;

	PRT_TRACE_BEGIN("\n");
	tmpreg.val = READREG32(&(g_mipi_csi->mcsi_cfg));
	PRT_TRACE_END("tmpreg.bits.busy=%d\n", tmpreg.bits.busy);

	return tmpreg.bits.busy;
}

/******************************************************************************
 * interface configure
 *****************************************************************************/

u32 csp_mcsi_isp_gat_lvl(void *base)
{
	reg_mipi_csi_lb_cfg_t tmpreg;
	reg_mipi_csi_t *g_mipi_csi = (reg_mipi_csi_t *)base;

	PRT_TRACE_BEGIN("\n");
	tmpreg.val = READREG32(&(g_mipi_csi->lb_cfg));
	tmpreg.bits.lvl = 0x20;
	WRITEREG32(&(g_mipi_csi->lb_cfg), tmpreg.val);
	PRT_TRACE_END("\n");

	return 0;
}

/**
 * enable mcsi output high 8bit data
 */
u32 csp_enable_mcsi_output_high_8bit_data(void *base)
{
	reg_mipi_csi_mcsi_cfg_t tmpreg;
	reg_mipi_csi_t *g_mipi_csi = (reg_mipi_csi_t *)base;

	tmpreg.val = READREG32(&(g_mipi_csi->mcsi_cfg));
	tmpreg.bits.data_8bit = 1;
	WRITEREG32(&(g_mipi_csi->mcsi_cfg), tmpreg.val);
	PRT_TRACE_END("\n");

	return 0;
}

/**
 * enable mcsi output raw msb data
 */
u32 csp_enable_mcsi_output_raw_msb_data(void *base)
{
	reg_mipi_csi_mcsi_cfg_t tmpreg;
	reg_mipi_csi_t *g_mipi_csi = (reg_mipi_csi_t *)base;

	tmpreg.val = READREG32(&(g_mipi_csi->mcsi_cfg));
	tmpreg.bits.raw_msb = 1;
	WRITEREG32(&(g_mipi_csi->mcsi_cfg), tmpreg.val);
	PRT_TRACE_END("\n");

	return 0;
}

/**
 * Select mcsi data path
 * @path:
 *	0: WB_DMA channel, by default
 *	1: Pixel channel to ISP
 *	2: Reserved
 *	3.VILD
 *	4.VILD and WB_DMA
 */
u32 csp_mcsi_data_path(void *base, u32 path)
{
	reg_mipi_csi_mcsi_cfg_t tmpreg;
	reg_mipi_csi_t *g_mipi_csi = (reg_mipi_csi_t *)base;

	PRT_TRACE_END("path=%d\n", path);
	tmpreg.val = READREG32(&(g_mipi_csi->mcsi_cfg));
	tmpreg.bits.path = path;
	WRITEREG32(&(g_mipi_csi->mcsi_cfg), tmpreg.val);
	if (path == MCSI_OUTPUT_PIXEL_CH)
		csp_mcsi_isp_gat_lvl(base);
	PRT_TRACE_END("\n");

	return 0;
}

u32 csp_mcsi_set_channel_size(void *base, u32 mipi_ch, u32 x, u32 y)
{
	reg_mipi_csi_ch_size_t tmpreg;
	unsigned long reg_addr = 0;
	reg_mipi_csi_t *g_mipi_csi = (reg_mipi_csi_t *)base;

	PRT_TRACE_BEGIN("x=%d,y=%d\n", x, y);
	tmpreg.val = 0;
	tmpreg.bits.width = x;
	tmpreg.bits.height = y;
	switch (mipi_ch) {
	case MCSI_CH0:
		reg_addr = (unsigned long)&g_mipi_csi->ch_size;
		WRITEREG32(reg_addr, tmpreg.val);
		break;
	default:
		break;

	}
	PRT_TRACE_END("\n");

	return 0;
}
/**
 * Select YUV componet sequence
 * @seq:
 * For YUV422 8-bit interleaved
 *	0: C0/C1/C2/C3
 *	1: C2/C1/C0/C3
 *	2: C1/C0/C3/C2
 *	3: C1/C2/C3/C0
 * In order to ensure YUV SP or Planar mode output correct data,
 * the YUV output component sequence should be adjusted to UYVY
 * If select no parse long packet, component sequence setup is useless
 */
u32 csp_mcsi_component_sequence(void *base, u32 seq)
{
	reg_mipi_csi_pkt_parse_t tmpreg;
	unsigned long reg_addr = 0;
	reg_mipi_csi_t *g_mipi_csi = (reg_mipi_csi_t *)base;

	PRT_TRACE_BEGIN("seq=%d\n", seq);
	reg_addr = (unsigned long)&g_mipi_csi->pkt_parse;
	tmpreg.val = READREG32(reg_addr);

	tmpreg.bits.c_seq = seq;
	WRITEREG32(reg_addr, tmpreg.val);
	PRT_TRACE_END("\n");

	return 0;
}

/**
 * Select long packet parse
 */
u32 csp_mcsi_parse_long_pkg(void *base, u32 en)
{
	reg_mipi_csi_pkt_parse_t tmpreg;
	unsigned long reg_addr = 0;
	reg_mipi_csi_t *g_mipi_csi = (reg_mipi_csi_t *)base;

	PRT_TRACE_BEGIN("\n");
	reg_addr = (unsigned long)&g_mipi_csi->pkt_parse;
	tmpreg.val = READREG32(reg_addr);
	tmpreg.bits.lp_parse = en;
	WRITEREG32(reg_addr, tmpreg.val);
	PRT_TRACE_END("\n");

	return 0;
}


/**
 * data identifier config
 * @dt: [5:0], Data type
 * @vc: [7:6], Virtual channel
 */
u32 csp_mcsi_data_identifier_config(void *base, u32 ch, u32 dt, u32 vc)
{
	unsigned long reg_addr = 0;
	reg_mipi_csi_di_cfg_t tmpreg;
	reg_mipi_csi_t *g_mipi_csi = (reg_mipi_csi_t *)base;

	reg_addr = (unsigned long)&g_mipi_csi->di_cfg;
	tmpreg.val = READREG32(reg_addr);
	switch (ch) {
	case 0:
		tmpreg.bits.ch0_dt = dt;
		tmpreg.bits.ch0_vc = vc;
		break;
	default:
		break;
	}
	WRITEREG32(reg_addr, tmpreg.val);

	return 0;
}

/**
 * Enable mcsi interrupt
 * @interrupt:
 */
void csp_mcsi_ints_enable(void *base, u32 interrput)
{
	reg_mipi_csi_int_en_t tmpreg;
	unsigned long reg_addr = 0;
	reg_mipi_csi_t *mipi_csi = (reg_mipi_csi_t *)base;

	reg_addr = (unsigned long)&mipi_csi->int_en;
	tmpreg.val = READREG32(reg_addr);

	tmpreg.val |= interrput;

	WRITEREG32(reg_addr, tmpreg.val);
}

/**
 * Disable mcsi interrupt
 */
void csp_mcsi_ints_disable(void *base, u32 interrput)
{
	reg_mipi_csi_int_en_t tmpreg;
	unsigned long reg_addr = 0;
	reg_mipi_csi_t *mipi_csi = (reg_mipi_csi_t *)base;

	reg_addr = (unsigned long)&mipi_csi->int_en;
	tmpreg.val = READREG32(reg_addr);

	tmpreg.val &= ~interrput;

	WRITEREG32(reg_addr, tmpreg.val);
}

/**
 * Read mcsi interrupts' pending
 */
u32 csp_mcsi_ints_pending(void *base)
{
	unsigned long reg_addr = 0;
	reg_mipi_csi_t *mipi_csi = (reg_mipi_csi_t *)base;

	reg_addr = (unsigned long)&mipi_csi->int_pend;

	return READREG32(reg_addr);
}

/**
 * Clear mcsi interrupt
 */
void csp_mcsi_ints_clear(void *base, u32 interrput)
{
	unsigned long reg_addr = 0;
	reg_mipi_csi_t *mipi_csi = (reg_mipi_csi_t *)base;

	reg_addr = (unsigned long)&mipi_csi->int_clr;
	WRITEREG32(reg_addr, interrput);
}

void csp_mcsi_contrl_ints_enable(void *base, u32 interrput)
{
	reg_mipi_csi_cntlr_int_en_t tmpreg;
	unsigned long reg_addr = 0;
	reg_mipi_csi_t *mipi_csi = (reg_mipi_csi_t *)base;

	reg_addr = (unsigned long)&mipi_csi->cntlr_int_en;
	tmpreg.val = READREG32(reg_addr);

	tmpreg.val |= interrput;

	WRITEREG32(reg_addr, tmpreg.val);
}

void csp_mcsi_contrl_ints_disable(void *base, u32 interrput)
{
	reg_mipi_csi_cntlr_int_en_t tmpreg;
	unsigned long reg_addr = 0;
	reg_mipi_csi_t *mipi_csi = (reg_mipi_csi_t *)base;

	reg_addr = (unsigned long)&mipi_csi->cntlr_int_en;
	tmpreg.val = READREG32(reg_addr);

	tmpreg.val &= ~interrput;

	WRITEREG32(reg_addr, tmpreg.val);
}

u32 csp_mcsi_contrl_ints_pending(void *base)
{
	unsigned long reg_addr = 0;
	reg_mipi_csi_t *mipi_csi = (reg_mipi_csi_t *)base;

	reg_addr = (unsigned long)&mipi_csi->cntlr_int_pend;

	return READREG32(reg_addr);
}

void csp_mcsi_contrl_ints_clear(void *base, u32 interrput)
{
	unsigned long reg_addr = 0;
	reg_mipi_csi_t *mipi_csi = (reg_mipi_csi_t *)base;

	reg_addr = (unsigned long)&mipi_csi->cntlr_int_clr;
	WRITEREG32(reg_addr, interrput);
}

void csp_mcsi_reg0_ints_enable(void *base, u32 interrput)
{
	reg_mipi_csi_mipi_int_en0_t tmpreg;
	unsigned long reg_addr = 0;
	reg_mipi_csi_t *mipi_csi = (reg_mipi_csi_t *)base;

	reg_addr = (unsigned long)&mipi_csi->mipi_int_en0;
	tmpreg.val = READREG32(reg_addr);

	tmpreg.val |= interrput;

	WRITEREG32(reg_addr, tmpreg.val);
}

void csp_mcsi_reg0_ints_disable(void *base, u32 interrput)
{
	reg_mipi_csi_mipi_int_en0_t tmpreg;
	unsigned long reg_addr = 0;
	reg_mipi_csi_t *mipi_csi = (reg_mipi_csi_t *)base;

	reg_addr = (unsigned long)&mipi_csi->mipi_int_en0;
	tmpreg.val = READREG32(reg_addr);

	tmpreg.val &= ~interrput;

	WRITEREG32(reg_addr, tmpreg.val);
}

u32 csp_mcsi_reg0_ints_pending(void *base)
{
	unsigned long reg_addr = 0;
	reg_mipi_csi_t *mipi_csi = (reg_mipi_csi_t *)base;

	reg_addr = (unsigned long)&mipi_csi->mipi_int_pend0;

	return READREG32(reg_addr);
}

void csp_mcsi_reg0_ints_clear(void *base, u32 interrput)
{
	unsigned long reg_addr = 0;
	reg_mipi_csi_t *mipi_csi = (reg_mipi_csi_t *)base;

	reg_addr = (unsigned long)&mipi_csi->mipi_int_clr0;
	WRITEREG32(reg_addr, interrput);
}

void csp_mcsi_reg1_ints_enable(void *base, u32 interrput)
{
	reg_mipi_csi_mipi_int_en1_t tmpreg;
	unsigned long reg_addr = 0;
	reg_mipi_csi_t *mipi_csi = (reg_mipi_csi_t *)base;

	reg_addr = (unsigned long)&mipi_csi->mipi_int_en1;
	tmpreg.val = READREG32(reg_addr);

	tmpreg.val |= interrput;

	WRITEREG32(reg_addr, tmpreg.val);
}

void csp_mcsi_reg1_ints_disable(void *base, u32 interrput)
{
	reg_mipi_csi_mipi_int_en1_t tmpreg;
	unsigned long reg_addr = 0;
	reg_mipi_csi_t *mipi_csi = (reg_mipi_csi_t *)base;

	reg_addr = (unsigned long)&mipi_csi->mipi_int_en1;
	tmpreg.val = READREG32(reg_addr);

	tmpreg.val &= ~interrput;

	WRITEREG32(reg_addr, tmpreg.val);
}

u32 csp_mcsi_reg1_ints_pending(void *base)
{
	unsigned long reg_addr = 0;
	reg_mipi_csi_t *mipi_csi = (reg_mipi_csi_t *)base;

	reg_addr = (unsigned long)&mipi_csi->mipi_int_pend1;

	return READREG32(reg_addr);
}

void csp_mcsi_reg1_ints_clear(void *base, u32 interrput)
{
	unsigned long reg_addr = 0;
	reg_mipi_csi_t *mipi_csi = (reg_mipi_csi_t *)base;

	reg_addr = (unsigned long)&mipi_csi->mipi_int_clr1;
	WRITEREG32(reg_addr, interrput);
}

void csp_mcsi_cfg_fifo(void)
{

}

#define MIPI_CSI_LBOR_BIT	13
#define MIPI_CSI_RST_BIT	3

u32 csp_mcsi_chk_lbor(void)
{
	/* u32 tmpval; */
	u32 ret = 0;

	return ret;
}

/**
 * Reset MIPI_CSI circuit
 */
u32 csp_mcsi_reset(void *base)
{
	reg_mipi_csi_mcsi_rst_t tmpreg;
	unsigned long reg_addr = 0;
	reg_mipi_csi_t *g_mipi_csi = (reg_mipi_csi_t *)base;

	PRT_TRACE_BEGIN("\n");
	reg_addr = (unsigned long)&g_mipi_csi->mcsi_rst;
	tmpreg.val = READREG32(reg_addr);
	tmpreg.bits.rst = 1;
	WRITEREG32(reg_addr, tmpreg.val);
	PRT_TRACE_END("\n");
	return 0;
}

/**
 * Config MIPI DPHY active lanes
 */
u32 csp_mipi_dphy_active_lane(void *base, u32 lane)
{
	reg_mipi_csi_dphy_ctrl_t tmpreg;
	unsigned long reg_addr = 0;
	reg_mipi_csi_t *g_mipi_csi = (reg_mipi_csi_t *)base;

	PRT_TRACE_BEGIN("lane=%d\n", lane);
	reg_addr = (unsigned long)&g_mipi_csi->dphy_ctrl;
	/* tmpreg.val = 0; */
	tmpreg.val = READREG32(reg_addr);
	tmpreg.bits.adl_config = lane - 1;
	WRITEREG32(reg_addr, tmpreg.val);
	PRT_TRACE_END("\n");

	return 0;
}

/**
 * MIPI DPHY shutdown
 */
u32 csp_mipi_dphy_shutdown(void *base)
{
	reg_mipi_csi_dphy_ctrl_t tmpreg;
	unsigned long reg_addr = 0;
	reg_mipi_csi_t *g_mipi_csi = (reg_mipi_csi_t *)base;

	PRT_TRACE_BEGIN("\n");
	reg_addr = (unsigned long)&g_mipi_csi->dphy_ctrl;
	tmpreg.val = READREG32(reg_addr);
	tmpreg.bits.pwrdown = 0;
	WRITEREG32(reg_addr, tmpreg.val);
	PRT_TRACE_END("\n");

	return 0;
}

/**
 * MIPI DPHY power up
 */
u32 csp_mipi_dphy_power_up(void *base)
{
	reg_mipi_csi_dphy_ctrl_t tmpreg;
	unsigned long reg_addr = 0;
	reg_mipi_csi_t *g_mipi_csi = (reg_mipi_csi_t *)base;

	PRT_TRACE_BEGIN("\n");
	reg_addr = (unsigned long)&g_mipi_csi->dphy_ctrl;
	tmpreg.val = READREG32(reg_addr);
	tmpreg.bits.pwrdown = 1;
	WRITEREG32(reg_addr, tmpreg.val);
	PRT_TRACE_END("\n");

	return 0;
}

/**
 * MIPI DPHY reset active
 */
u32 csp_mipi_dphy_reset_active(void *base)
{
	reg_mipi_csi_dphy_ctrl_t tmpreg;
	reg_mipi_csi_t *g_mipi_csi = (reg_mipi_csi_t *)base;

	PRT_TRACE_BEGIN("\n");
	tmpreg.val = 0;
	tmpreg.bits.rst = 0;
	WRITEREG32(&(g_mipi_csi->dphy_ctrl), tmpreg.val);
	PRT_TRACE_END("\n");

	return 0;
}

/**
 * MIPI DPHY reset in-active
 */
u32 csp_mipi_dphy_reset_inactive(void *base)
{
	reg_mipi_csi_dphy_ctrl_t tmpreg;
	unsigned long reg_addr = 0;
	reg_mipi_csi_t *g_mipi_csi = (reg_mipi_csi_t *)base;

	PRT_TRACE_BEGIN("\n");
	reg_addr = (unsigned long)&g_mipi_csi->dphy_ctrl;
	tmpreg.val = READREG32(reg_addr);
	tmpreg.bits.rst = 1;
	WRITEREG32(reg_addr, tmpreg.val);
	PRT_TRACE_END("\n");

	return 0;
}

/**
 * MIPI DPHY controller reset active
 */
u32 csp_mipi_dphy_controller_reset_active(void *base)
{
	reg_mipi_csi_csi2_cntlr_rst_t tmpreg;
	reg_mipi_csi_t *g_mipi_csi = (reg_mipi_csi_t *)base;

	PRT_TRACE_BEGIN("\n");
	tmpreg.val = 0;
	tmpreg.bits.rst = 0;
	WRITEREG32(&(g_mipi_csi->csi2_cntlr_rst), tmpreg.val);
	PRT_TRACE_END("\n");

	return 0;
}

/**
 * MIPI DPHY controller reset in-active
 */
u32 csp_mipi_dphy_controller_reset_inactive(void *base)
{
	reg_mipi_csi_csi2_cntlr_rst_t tmpreg;
	unsigned long reg_addr = 0;
	reg_mipi_csi_t *g_mipi_csi = (reg_mipi_csi_t *)base;

	PRT_TRACE_BEGIN("\n");
	reg_addr = (unsigned long)&g_mipi_csi->csi2_cntlr_rst;
	tmpreg.val = 0;
	tmpreg.bits.rst = 1;
	WRITEREG32(reg_addr, tmpreg.val);
	PRT_TRACE_END("\n");

	return 0;
}

/**
 * MIPI DLANE status
 */
u32 csp_mipi_dlane_state(void *base)
{
	reg_mipi_csi_dlane_state_t tmpreg;
	reg_mipi_csi_t *g_mipi_csi = (reg_mipi_csi_t *)base;

	PRT_TRACE_BEGIN("\n");
	tmpreg.val = READREG32(&(g_mipi_csi->dlane_state));
	PRT_TRACE_END("tmpreg.val=%d\n", tmpreg.val);

	return tmpreg.val;
}

u32 csp_mipi_clane_state(void *base)
{
	reg_mipi_csi_clane_state_t tmpreg;
	reg_mipi_csi_t *g_mipi_csi = (reg_mipi_csi_t *)base;

	PRT_TRACE_BEGIN("\n");
	tmpreg.val = READREG32(&(g_mipi_csi->clane_state));
	PRT_TRACE_END("tmpreg.val=%d\n", tmpreg.val);

	return tmpreg.val;
}

/**
 * Config MIPI DPHY data ID
 * @id: data id
 * @dt: [5:0], Data type
 * @vc: [7:6], Virtual channel
 */
u32 csp_mipi_dphy_data_id(void *base, u32 id, u32 dt, u32 vc)
{
	reg_mipi_csi_data_ids_t tmpreg;
	unsigned long reg_addr = 0;
	reg_mipi_csi_t *g_mipi_csi = (reg_mipi_csi_t *)base;

	PRT_TRACE_BEGIN("id=%d,dt=%d,vc=%d\n", id, dt, vc);
	reg_addr = (unsigned long)&g_mipi_csi->data_ids;
	tmpreg.val = READREG32(reg_addr);
	/* tmpreg.val = READREG32(&(g_mipi_csi->data_ids)); */
	switch (id) {
	case 0:
		tmpreg.bits.di0_dt = dt;
		tmpreg.bits.di0_vc = vc;
		break;
	case 1:
		tmpreg.bits.di1_dt = dt;
		tmpreg.bits.di1_vc = vc;
		break;
	case 2:
		tmpreg.bits.di2_dt = dt;
		tmpreg.bits.di2_vc = vc;
		break;
	case 3:
		tmpreg.bits.di3_dt = dt;
		tmpreg.bits.di3_vc = vc;
		break;
	default:
		break;
	}
	WRITEREG32(reg_addr, tmpreg.val);
	PRT_TRACE_END("\n");

	return 0;
}

typedef struct dphy_freq_map {
	u32 freq;
	u32 freq_code;
} dphy_freq_map_t;

static dphy_freq_map_t dphy_freq_table[] = {
	{90000000,   0x00},
	{99000000,   0x10},
	{108000000, 0x20},
	{123000000, 0x01},
	{135000000, 0x11},
	{150000000, 0x21},
	{159000000, 0x02},
	{180000000, 0x12},
	{198000000, 0x22},
	{210000000, 0x03},
	{240000000, 0x13},
	{249000000, 0x23},
	{270000000, 0x04},
	{300000000, 0x14},
	{330000000, 0x05},
	{360000000, 0x15},
	{399000000, 0x25},
	{450000000, 0x06},
	{486000000, 0x16},
	{549000000, 0x07},
	{600750000, 0x17},
	{648000000, 0x08},
	{702000000, 0x18},
	{756000000, 0x09},
	{783000000, 0x19},
	{850500000, 0x0A},
	{904500000, 0x1A},
	{972000000, 0x2A},
	{999000000, 0x3A},
	{1053000000, 0x0B},
	{1107000000, 0x1B},
	{1147500000, 0x2B},
	{1188000000, 0x3B},
};

/**
 * Read MIPI DPHY register
 * @addr: register address to read
 *
 * return the read value
 */
u32 csp_mipi_dphy_read(u8 addr)
{
	u32 rval = 0;
	return rval;
}

/**
 * Write MIPI DPHY register
 * @addr: register address to read
 * @wval: write value
 */
u32 csp_mipi_dphy_write(u8 addr, u8 wval)
{
	return 0;
}

void mipi_dphy_rw_wait(void *base, u8 addr)
{
	unsigned long reg_addr;
	unsigned long delay;
	ktime_t start, now;
	int spins;
	reg_mipi_csi_t *g_mipi_csi = (reg_mipi_csi_t *)base;

	reg_addr = (unsigned long)&g_mipi_csi->dphy_if_cfg;
	spins = 3;
	delay = 1;
	start = now = ktime_get();

	while (--spins && (READREG32(reg_addr) & 1))
		cpu_relax();

	while ((READREG32(reg_addr) & 1) &&
	       ktime_us_delta(now, start) < DHY_WAIT_TIMEOUT) {
		usleep_range(delay, 2 * delay);
		if (delay < DHY_WAIT_TIMEOUT / 10)
			delay <<= 1;
		now = ktime_get();
	}

	if (READREG32(reg_addr) & 1)
		PRT_WARN("timeout in %s\n", __func__);

}

u32 mipi_dphy_read(void *base, u8 addr, u8 set)
{
	u32 rval;
	unsigned long reg_addr = 0;
	reg_mipi_csi_t *g_mipi_csi = (reg_mipi_csi_t *)base;

	reg_addr = (unsigned long)&g_mipi_csi->dphy_if_cfg;

	WRITEREG32(reg_addr, (set << 22) | (addr << 8));
	WRITEREG32(reg_addr, (set << 22) | (addr << 8) | 1);
	mipi_dphy_rw_wait(base, addr);

	rval = (READREG32(reg_addr)>>24)&0xFF;
	return rval;
}


void mipi_dphy_write(void *base, u8 addr, u8 wval)
{
	unsigned long reg_addr = 0;
	reg_mipi_csi_t *g_mipi_csi = (reg_mipi_csi_t *)base;

	reg_addr = (unsigned long)&g_mipi_csi->dphy_if_cfg;

	WRITEREG32(reg_addr, (wval << 16) | (addr << 8) | (1 << 1));
	WRITEREG32(reg_addr, (wval << 16) | (addr << 8) | (1 << 1)|1);
	mipi_dphy_rw_wait(base, addr);
}

int mipi_dphy_rx_stable_wait(void *base, u8 addr, u8 set, u32 wval)
{
	unsigned long reg_addr;
	unsigned long delay;
	ktime_t start, now;
	int spins;
	reg_mipi_csi_t *g_mipi_csi = (reg_mipi_csi_t *)base;

	reg_addr = (unsigned long)&g_mipi_csi->dphy_if_cfg;
	spins = 3;
	delay = 1;
	start = now = ktime_get();

	while (--spins) {
		if (mipi_dphy_read(base, addr, set) == wval)
			return 0;

		cpu_relax();
	}

	while (ktime_us_delta(now, start) < DHY_WAIT_TIMEOUT) {
		if (mipi_dphy_read(base, addr, set) == wval)
			return 0;

		usleep_range(delay, 2 * delay);
		if (delay < DHY_WAIT_TIMEOUT / 10)
			delay <<= 1;
		now = ktime_get();
	}

	if (mipi_dphy_read(base, addr, set) == wval)
		return 0;
	else
		return -EAGAIN;
}
/**
 * mipi_dphy_rx_stable
 * @csi_id: MCSI module ID
 * @lane_num: MCSI used data lanes num
 */
void mipi_dphy_rx_stable(void *base, u32 csi_id, u32 lane_num)
{
	int ret = 0;
	int lane_idx = lane_num;

	/* clock lane */
	ret = mipi_dphy_rx_stable_wait(base, 0x3F, 0, 0xE);
	if (ret != 0)
		PRT_ERR("wait clk lane stable fail\n");

	if (csi_id == 1 || csi_id == 3) {
		lane_idx = lane_idx + 2;
		goto MIPI_CSI1OR3_DPHY_STATUS;
	}

	/* data lane 0 */
	ret = mipi_dphy_rx_stable_wait(base, 0x4F, 0, 0xE);
	if (ret != 0)
		PRT_ERR("wait data lane0 stable fail\n");

	/* data lane 1 */
	if (lane_idx > 1) {
		ret = mipi_dphy_rx_stable_wait(base, 0x5F, 0, 0xE);
		if (ret != 0)
			PRT_ERR("wait data lane1 stable fail\n");
	}

MIPI_CSI1OR3_DPHY_STATUS:
	/* data lane 2 */
	if (lane_idx > 2) {
		ret = mipi_dphy_rx_stable_wait(base, 0x8F, 0, 0xE);
		if (ret != 0)
			PRT_ERR("wait data lane2 stable fail\n");
	}

	/* data lane 3 */
	if (lane_idx > 3) {
		ret = mipi_dphy_rx_stable_wait(base, 0x9F, 0, 0xE);
		if (ret != 0)
			PRT_ERR("wait data lane3 stable fail\n");
	}
}
/*
 * MIPI DPHY Init
 * @lane: data lane count
 * @id: data id
 * @dt: Data type
 * @vc: Virtual channel
 * @freq: frequency for MIPI DPHY
 */
u32 csp_mipi_dphy_init(void *base, u32 lane, u32 id, u32 dt, u32 vc,
			u32 freq, u32 csi_id)
{
	u32 code, i;
	u32 rval;
	u8 dphy_r[4][2] = {
		{0x41, 0x2},
		{0x41, 0x2},
		{0x51, 0x2},
		{0x51, 0x2},
	};
	u8 dphy_w[4][3] = {
		{0x41, 0x77, 0x88},
		{0x41, 0x7B, 0x84},
		{0x51, 0x77, 0x88},
		{0x51, 0x7B, 0x84},
	};

	if (csi_id == 1 || csi_id == 3) {
		if (lane > 2) {
			PRT_ERR("MCSI%d dlanes two at most!\n", csi_id);
			lane = 2;
		}
	}

	csp_mipi_dphy_active_lane(base, lane);

	csp_mipi_dphy_data_id(base, id, dt, vc);

	/* find the matched frequency */
	for (i = 0; i < ARRAY_SIZE(dphy_freq_table); i++) {
		if (dphy_freq_table[i].freq >= freq)
			break;
	}
	if (i < ARRAY_SIZE(dphy_freq_table))
		code = dphy_freq_table[i].freq_code;
	else
		code = 0;

	/* only for BG0806 */
	/*
	 * FORCE CLOCK MISS DISABLE
	 * mipi_dphy_write(0x35, 0x02);
	 * rval = mipi_dphy_read(0x35, 0x00);
	 */

	mipi_dphy_write(base, 0x00, 0x1);

	PRT_TRACE_BEGIN("==============code %u=============\n", code);
	mipi_dphy_write(base, 0x0F, code);

	csp_mipi_dphy_controller_reset_inactive(base);
	csp_mipi_dphy_power_up(base);
	csp_mipi_dphy_reset_inactive(base);
	mipi_dphy_rx_stable(base, csi_id, lane);

	for (i = 0; i < ARRAY_SIZE(dphy_r); i++) {
		rval = mipi_dphy_read(base, dphy_r[i][0], dphy_r[i][1]);
		PRT_DBG("before %d write 0x%x is %x\n", i, dphy_r[i][0], rval);
		mipi_dphy_write(base, dphy_w[i][0],
				(rval & dphy_w[i][1]) | dphy_w[i][2]);
		rval = mipi_dphy_read(base, dphy_r[i][0], dphy_r[i][1]);
		PRT_DBG("after %d write 0x%x is %x\n", i, dphy_r[i][0], rval);
	}

	PRT_TRACE_END("\n");

	return 0;
}
/**
 * csp_mcsi_dump_regs - dump all the register
 * @func_name:	the name of the function that want dump registers
 */
void csp_mcsi_dump_regs(const char *func_name)
{
/*
 *	u32 offset = 0;
 *
 *	PRT_DBG(" =========== (%s) ===========\n",
 *		func_name);
 *
 *	for (offset = 0; offset < 0x1CC; offset += 8) {
 *		PRT_DBG(" 0x%x: 0x%08x | 0x%x: 0x%08x\n",
 *			offset, READREG32((u32)g_mipi_csi + offset),
 *			offset + 4, READREG32((u32)g_mipi_csi + offset + 4));
 *	}
 *
 *
 *	for (offset = 0x600; offset < 0x610; offset += 8) {
 *		PRT_DBG(" 0x%x: 0x%08x | 0x%x: 0x%08x\n",
 *			offset, READREG32((u32)g_mipi_csi + offset),
 *			offset + 4, READREG32((u32)g_mipi_csi + offset + 4));
 *	}
 *
 *	for (offset = 0x1004; offset < 0x1064; offset += 8) {
 *		PRT_DBG(" 0x%x: 0x%08x | 0x%x: 0x%08x\n",
 *			offset, READREG32((u32)g_mipi_csi + offset),
 *			offset + 4, READREG32((u32)g_mipi_csi + offset + 4));
 *	}
 *	PRT_DBG(" ===========================================\n");
 */
}
